import React, { memo, useMemo } from 'react';
import { Handle, Position } from '@xyflow/react';
import { Box, Typography, Paper, Chip, type SxProps, type Theme } from '@mui/material';
import { ErrorOutline as ErrorIcon } from '@mui/icons-material';
import { NodeType, type BehaviorTreeNode } from '../../types/behaviorTree';
import { useFlowStore } from '../../store/flowStore';
import { NODE_COLORS, ERROR_COLORS, HANDLE_STYLE } from '../../constants/theme';

interface BaseNodeProps {
  id: string;
  data: BehaviorTreeNode['data'];
  type: NodeType;
  icon: React.ReactElement;
  defaultColors: typeof NODE_COLORS[NodeType];
}

/**
 * 基础节点组件
 * 所有行为树节点的通用实现，提供统一的样式和错误状态显示
 * 使用 React.memo 优化性能，只在关键 props 变化时重新渲染
 */
const BaseNodeComponent: React.FC<BaseNodeProps> = ({ 
  id, 
  data, 
  type, 
  icon, 
  defaultColors 
}) => {
  // 订阅整个 errors 数组，引用是稳定的
  const errors = useFlowStore((state) => state.errors);
  
  // 在组件内部使用 useMemo 派生出当前节点的错误，避免不必要的重渲染
  const nodeErrors = useMemo(
    () => errors.filter(err => err.nodeId === id),
    [errors, id]
  );
  
  // 使用 useMemo 缓存计算结果
  const hasError = useMemo(
    () => nodeErrors.some(err => err.type === 'error'),
    [nodeErrors]
  );
  
  const hasWarning = useMemo(
    () => nodeErrors.some(err => err.type === 'warning'),
    [nodeErrors]
  );

  const borderColor = hasError 
    ? ERROR_COLORS.error.border 
    : hasWarning 
    ? ERROR_COLORS.warning.border 
    : defaultColors.border;
    
  const backgroundColor = hasError 
    ? ERROR_COLORS.error.background 
    : hasWarning 
    ? ERROR_COLORS.warning.background 
    : defaultColors.background;

  const chipBackgroundColor = hasError
    ? ERROR_COLORS.error.chipBackground
    : hasWarning
    ? ERROR_COLORS.warning.chipBackground
    : defaultColors.chipBackground;

  const chipTextColor = hasError
    ? ERROR_COLORS.error.chipText
    : hasWarning
    ? ERROR_COLORS.warning.chipText
    : defaultColors.chipText;

  // 使用 useMemo 缓存 sx 对象，避免每次渲染时重新创建
  const paperSx = useMemo<SxProps<Theme>>(() => ({
    padding: 1.5,
    borderRadius: '8px',
    border: `2px solid ${borderColor}`,
    backgroundColor,
    color: 'white',
    textAlign: 'center',
    minWidth: 120,
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    position: 'relative',
  }), [borderColor, backgroundColor]);

  const chipSx = useMemo<SxProps<Theme>>(() => ({
    backgroundColor: chipBackgroundColor,
    color: chipTextColor,
    fontWeight: 'bold',
  }), [chipBackgroundColor, chipTextColor]);

  const errorIconSx = useMemo<SxProps<Theme>>(() => ({
    color: '#ffeb3b',
    fontSize: 18,
    ml: 0.5,
    position: 'absolute',
    top: 5,
    right: 5,
  }), []);

  return (
    <Paper
      elevation={3}
      sx={paperSx}
      role="region"
      aria-label={`${type} 节点: ${data.label}`}
      aria-describedby={hasError ? `error-${id}` : undefined}
    >
      <Handle type="target" position={Position.Top} style={HANDLE_STYLE} />
      <Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
        {icon}
        <Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
          {type.toUpperCase()}
        </Typography>
        {hasError && (
          <>
            <Typography id={`error-${id}`} className="sr-only">
              此节点存在错误
            </Typography>
            <ErrorIcon sx={errorIconSx} />
          </>
        )}
      </Box>
      <Chip
        label={data.label}
        size="small"
        sx={chipSx}
      />
      <Handle type="source" position={Position.Bottom} style={HANDLE_STYLE} />
    </Paper>
  );
};

// 使用 React.memo 包装组件，自定义比较函数
export const BaseNode = memo(BaseNodeComponent, (prevProps, nextProps) => {
  // 只有在关键 props 变化时才重新渲染
  if (prevProps.id !== nextProps.id) return false;
  if (prevProps.type !== nextProps.type) return false;
  if (prevProps.data.label !== nextProps.data.label) return false;
  
  // 深度比较 data 对象（对于小对象，这是可以接受的）
  // 如果性能成为问题，可以考虑使用浅比较或者更精细的比较逻辑
  const prevKeys = Object.keys(prevProps.data);
  const nextKeys = Object.keys(nextProps.data);
  
  if (prevKeys.length !== nextKeys.length) return false;
  
  return prevKeys.every(key => prevProps.data[key] === nextProps.data[key]);
});

